package com.eonmind.vip.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.eonmind.common.contract.application.redis.service.RedisService;
import com.eonmind.common.contract.data.response.ResponseEntity;
import com.eonmind.common.contract.timer.Timer;
import com.eonmind.common.provider.entity.vip.PointsConsumption;
import com.eonmind.common.provider.entity.vip.UserWelfare;
import com.eonmind.common.provider.entity.vip.VipManagement;
import com.eonmind.user.contract.dto.UserInfoData;
import com.eonmind.vip.contract.constants.CommonConstant;
import com.eonmind.vip.contract.constants.RedisStrConstant;
import com.eonmind.vip.dto.ClockDto;
import com.eonmind.vip.mapper.PointsMapper;
import com.eonmind.vip.mapper.UserWelfareMapper;
import com.eonmind.vip.mapper.VipManagementMapper;
import com.eonmind.vip.provider.VipToUserServerProvider;
import com.eonmind.vip.service.ClockService;
import jakarta.annotation.Resource;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

@Service
public class ClockServiceImpl implements ClockService {

    @Resource
    private RedisService redisService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private UserWelfareMapper userWelfareMapper;

    @Resource
    private PointsMapper pointsMapper;

    @Resource
    private VipToUserServerProvider vipToUserServerProvider;

    @Override
    @Transactional
    public ResponseEntity<Integer> userClock(UserInfoData userInfoData) {
        // 判断今日是否已经签到
        ResponseEntity<Boolean> isClock = isUserClocked(userInfoData);
        Long userId = userInfoData.getId();
        if (isClock.getData()) {
            return ResponseEntity.fail("已签到");
        }
        LocalDate today = LocalDate.now();
        String key = getSignKey(userInfoData);
        int day = today.getDayOfMonth() - 1; // 确定当前天的索引


        int points = CommonConstant.SIGN_BASIC_POINT;
        UserWelfare userWelfare = userWelfareMapper.getUserPlanByUserID(userId);
        if (ObjectUtil.isEmpty(userWelfare)) {
            vipToUserServerProvider.initUserWelfare(userId);
            userWelfare = userWelfareMapper.getUserPlanByUserID(userId);
        }
        int dailyCoins = userWelfare.getCachedFeatures().getDailyCoins() == null ? 0 : userWelfare.getCachedFeatures().getDailyCoins();
        Integer total = (userWelfare.getCachedFeatures().getTotalCoins() == null ? 0 :  userWelfare.getCachedFeatures().getTotalCoins()) + dailyCoins + points;
        Integer allCoins = (userWelfare.getCachedFeatures().getAllCoins() == null ? 0 : userWelfare.getCachedFeatures().getAllCoins()) + dailyCoins + points;
        UserWelfare userWelfare1 = new UserWelfare()
                .setUpdateTime(Timer.currentTimestamp())
                .setCachedFeatures(
                        new UserWelfare.CachedFeatures()
                                .setTotalCoins(total)
                                .setAllCoins(allCoins)
                                .setFeatures(new UserWelfare.CachedFeatures.Features())
                );
        try {
            redisService.setBitMap(key, day);
            userWelfareMapper.updateUserPlanByUserID(userId, userWelfare1);
            PointsConsumption pointsConsumption = new PointsConsumption().setConsumption(+100)
                    .setCategory("获取")
                    .setUserID(userId)
                    .setBalance(total)
                    .setType(4)
                    .setCreateTime(Timer.currentTimestamp())
                    .setDescription("签到获取100积分")
                    .setUpdateTime(Timer.currentTimestamp());
            if (dailyCoins != 0) {
                PointsConsumption pointsConsumption1 = new PointsConsumption().setConsumption(+dailyCoins)
                        .setCategory("获取")
                        .setUserID(userId)
                        .setBalance(total)
                        .setType(4)
                        .setCreateTime(Timer.currentTimestamp())
                        .setDescription("会员权益领取"+dailyCoins+"积分")
                        .setUpdateTime(Timer.currentTimestamp());
                pointsMapper.save(pointsConsumption1);
            }
            pointsMapper.save(pointsConsumption);
        } catch (Exception e) {
            redisService.remove(key);
            throw new RuntimeException("操作失败，已经回滚 redis ", e);
        }
        // 将获取到的签到积分存入数据库
        return ResponseEntity.success("签到成功", points + dailyCoins);
    }

    @Override
    public ResponseEntity<Integer> getConsecutiveClockInDays(UserInfoData userInfoData) {
        int consecutiveDays = consecutiveSignInDays(getSignKey(userInfoData));
        return ResponseEntity.success("连续签到天数", consecutiveDays);
    }

    @Override
    public ResponseEntity<List<ClockDto.ClockRecordsResponse>> getMonthlyClockInRecords(UserInfoData userInfoData) {
        String key = getSignKey(userInfoData);
        LocalDate today = LocalDate.now();
        int lengthOfMonth = LocalDate.now().lengthOfMonth();
        int currentDay = today.getDayOfMonth();
        List<Long> bitFieldList = (List<Long>) redisTemplate.execute(
                (RedisCallback<List<Long>>) cbk ->
                        cbk.bitField(key.getBytes(), BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(lengthOfMonth)).valueAt(0))
        );
        List<String> resultList = new ArrayList<>();
        HashSet<Integer> clockedSet = new HashSet<Integer>();
        if (bitFieldList != null && !bitFieldList.isEmpty()) {
            long valueDec = bitFieldList.get(0) != null ? bitFieldList.get(0) : 0;

            for (int i = lengthOfMonth; i > 0; i--) {
                LocalDate tempDayOfMonth = LocalDate.now().withDayOfMonth(i);
                if (valueDec >> 1 << 1 != valueDec) {
                    resultList.add(tempDayOfMonth.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
                    clockedSet.add(i);
                }
                valueDec >>= 1;
            }
        }
        List<ClockDto.ClockRecordsResponse> res = new ArrayList<>();
        for (int i = lengthOfMonth; i > 0; i--) {
            LocalDate tempDayOfMonth = LocalDate.now().withDayOfMonth(i);
            DayOfWeek dayOfWeek = tempDayOfMonth.getDayOfWeek();
            res.add(0,
                    new ClockDto.ClockRecordsResponse()
                            .setIsClock(clockedSet.contains(i))
                            .setDate(tempDayOfMonth.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
                            .setIsToday(i == currentDay)
                            .setIsAfterToday(i > currentDay)
                            .setWeekday(dayOfWeek.name())
            );
        }



        return ResponseEntity.success(res);
    }

    @Override
    public ResponseEntity<Boolean> isUserClocked(UserInfoData userInfoData) {
        String key = getSignKey(userInfoData);
        LocalDate today = LocalDate.now();
        Boolean isSign = redisService.getBitByIndex(key, today.getDayOfMonth() - 1);
        return ResponseEntity.success(isSign);
    }

    private String getSignKey(UserInfoData userInfoData) {
        LocalDate today = LocalDate.now();
        return RedisStrConstant.USER_SIGN + userInfoData.getId() + ":"
                + today.getYear() + ":" + today.getMonthValue();
    }

    private int consecutiveSignInDays(String key) {
        LocalDate today = LocalDate.now();
        int lengthOfMonth = LocalDate.now().lengthOfMonth();
        int day = today.getDayOfMonth() - 1; // 确定当前天的索引
        List<Long> bitFieldList = (List<Long>) redisTemplate.execute(
                (RedisCallback<List<Long>>) cbk ->
                        cbk.bitField(key.getBytes(), BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(lengthOfMonth)).valueAt(0))
        );
        int consecutiveDays = 0;
        if (bitFieldList != null && !bitFieldList.isEmpty()) {
            long valueDec = bitFieldList.get(0) != null ? bitFieldList.get(0) : 0;
            int offset = lengthOfMonth - day;
            valueDec >>= offset;
            if (valueDec > 0) {
                while (valueDec >> 1 << 1 != valueDec) {
                    consecutiveDays++;
                    valueDec >>= 1;
                }
            }
        }
        return consecutiveDays;
    }
}
